package bankrmi;

import java.sql.Statement;
import java.sql.SQLException;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BankImpl extends UnicastRemoteObject
        implements bankrmi.Bank {

    private static final String DATASOURCE = "bankbdd";
    private static final String DBMS = "mysql";
    private static final String tableName = "client";
    private String _bankname = "suedenBank";
    private Hashtable _accounts = new Hashtable();
    private String datasource;
    private String dbms;
    private Statement sqlStatement;

    public static void main(String[] args) {
        try {
            Bank bankobj = new BankImpl("suedenBank", DATASOURCE, DBMS);
            // Register the newly created object at rmiregistry.
            Naming.rebind("rmi://localhost/suedenBank", bankobj);
            System.out.println(bankobj + " is ready.");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /** Construct a persistently named object. */
    public BankImpl(String name, String datasource, String dbms) throws RemoteException {
        super();
        _bankname = name;
        this.dbms = dbms;
        this.datasource = datasource;
        createDatasource();
    }

    private void createDatasource() {
        try {
            Connection connection = getConnection();
            System.out.println("connected to the BDD");
            sqlStatement = connection.createStatement();
            boolean exist = false;

            DatabaseMetaData dbm = connection.getMetaData();
            for (ResultSet rs = dbm.getTables(null, null, "client", null); rs.next();) {
                exist = true;
                rs.close();
                System.out.println("the table already exists");
                break;
            }
            if (!exist) {
                System.out.println("create a new table named client");
                sqlStatement.executeUpdate("CREATE TABLE client (id int PRIMARY KEY, name VARCHAR(20), password VARCHAR(50), balance float)");
            }
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(BankImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException ex) {
            Logger.getLogger(BankImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private Connection getConnection() throws ClassNotFoundException, SQLException {
        if (dbms.equalsIgnoreCase("mysql")) {
            Class.forName("com.mysql.jdbc.Driver");
            return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/" + datasource, "root", "");
        } else {
            return null;
        }
    }

    /** Construct a transient object. */
    public BankImpl() throws RemoteException {
        super();
    }

    @Override
    public synchronized Account newAccount(String name, String password, float amountMoney)
            throws RemoteException, Rejected {
        AccountImpl account = (AccountImpl) _accounts.get(name);
        if (account != null) {
            System.out.println("Account [" + name + "] exists!!!");
            throw new Rejected("Rejected: Bank: "
                    + _bankname
                    + " Account for: "
                    + name
                    + " already exists: "
                    + account);
        }

        ResultSet result = null;
        try {
            result = sqlStatement.executeQuery("SELECT * from " + tableName + " WHERE NAME='" + name + "'");

            if (result.next()) {
                //account exists, instantiate, put in cache and throw exception
                account = new AccountImpl(name, password);
                _accounts.put(name, account);
                throw new Rejected("account already exists in the bank DBB");
            }
            result.close();

            //create new account 
            System.out.println("this account exists no where so I create a new account");
            int rows = sqlStatement.executeUpdate("INSERT INTO " + tableName + " VALUES (0,'" + name + "', '" + password + "', '" + amountMoney + "')");
            if (rows == 1) {
                account = new AccountImpl(name, password);
                _accounts.put(name, account);
                System.out.println("account has been created for " + name);
                return account;
            } else {
                throw new Rejected("cannot create account in the DBB");
            }

        } catch (SQLException ex) {
            Logger.getLogger(BankImpl.class.getName()).log(Level.SEVERE, null, ex);
        }

        return account;
    }

    @Override
    public synchronized Account getAccount(String name, String password)
            throws RemoteException, bankrmi.Rejected {

        if (name == null) {
            return null;
        }

        AccountImpl acc = (AccountImpl) _accounts.get(name);
        if (acc != null && !password.equalsIgnoreCase(acc.getPassword())) {
            return null;
        }
        else if (acc == null) {
            //we try in the DBB
            System.out.println("nothing in the bank cache");
            try {
                ResultSet result = sqlStatement.executeQuery("SELECT * FROM " + tableName + " WHERE name= '" + name + "' AND password= '" + password + "'");

                if (result.next()) {
                    if (password.equalsIgnoreCase(result.getString("password"))) {
                        acc = new AccountImpl(result.getString("name"), result.getString("password"), result.getFloat("balance"));
                        result.close();
                        _accounts.put(name, acc);
                    } else {
                        System.out.println("wrong password");
                        return null;
                    }
                } else {
                    System.out.println("nothing in the bank dbb");
                    result.close();
                    return null;
                }
            } catch (SQLException e) {
                throw new Rejected("unable to find the bank account for " + name);
            }
        }
        System.out.println("in bank: get account from the bank " + acc);
        return acc;
    }

    @Override
    public synchronized boolean deleteAccount(String name)
            throws RemoteException, bankrmi.Rejected {
        try {
            if (!hasAccount(name)) {
                return false;
            }
            _accounts.remove(name);
            int rows = sqlStatement.executeUpdate("DELETE FROM " + tableName + " WHERE name='" + name + "'");
            if (rows != 1) {
                throw new Rejected("Unable to delete account....");
            }
        } catch (SQLException ex) {
            Logger.getLogger(BankImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new Rejected("Unable to delete account....");
        }

        System.out.println("Bank account for " + name + " has been deleted");
        return true;
    }

    private boolean hasAccount(String name) {
        if (_accounts.get(name) == null) {
            return false;
        } else {
            return true;
        }
    }
}
